#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/8/2 13:27
# @Author  : huidong.bai
# @File    : TSASession.py
# @Software: PyCharm
# @Mail    : MasterBai2018@outlook.com
import pdb
import os
import re
import time
import json
import allure
import decimal
import threading
from ctypes import *
from threading import Event
from conftest import logging
from datetime import datetime
from src.utils.common import uuid
from src.utils.jsonUtil import JsonUtil
from src.utils.common import async_color_print, color_print, pop
from src.utils.common import method, get_timestamp
from src.core.Common.Bean.RuntimeRecorder import RuntimeRecorder
from src.core.Status.AIBSSessionStatus import AIBSStatusCode, AIBSParam, AIBSVRConfigCode, MongoCode


# 定义回调结构体
class AIBSResultST(Structure):
    _fields_ = [
        ("flag", c_int),      # 回调音频能量时，表示data的长度
        ("data", c_char_p),   # 回调数据
        ("type", POINTER(c_char) * 128),   # 回调类型
    ]


# 定义多通道音频数据结构体
class AIBSSpeechDataST(Structure):
    _fields_ = [
        ("mic_data", POINTER(c_char) * 4),
        ("ref_data", POINTER(c_char)),
        ("output", c_char_p),
        ("direction", c_int)
    ]


# 定义输入法回调结构体
class AIBSKeybordResultST(Structure):
    _fields_ = [
        ("data", c_char_p)
    ]


class TSASession:
    def __init__(self, lib_path: str):
        self.default_language = None
        self.m_callback = None
        self.m_config = None
        self.m_caseList = None
        self.m_data_path = None
        self.m_case_num = 0
        self.m_lang = ""
        self.m_digit_flag = False
        self.m_engine = None
        self.m_library = None
        self.m_status = 0
        self.m_delay = 1.0
        self.m_sampleRate = 16000
        self.m_speed = 1
        self.m_index = -1
        self.m_start_flag = 0
        self.m_stop_flag = 0
        self.speech_end_time = 0
        self.m_personal_tag = 0
        self.m_process_timescale_callback = None
        self.m_assert_finish_cond: threading.Event()
        self.m_recorder = None
        self.m_lock = threading.Lock()
        self.m_create_cond = threading.Condition(self.m_lock)
        self.m_start_cond = threading.Condition(self.m_lock)
        self.m_finish_cond = threading.Condition(self.m_lock)
        self.m_personal_cond = threading.Condition(self.m_lock)
        self.m_freeWakeup_cond = threading.Condition(self.m_lock)

        self.m_dialog = 0
        self.m_region_list = []

        self.m_voice_start_tag = 0
        self.m_voice_end_tag = 0
        self.m_cancel_time = -1
        self.m_cancel_event = None
        self.m_timestamp = 0

        try:
            self.m_library = cdll.LoadLibrary(lib_path)
        except Exception as e:
            logging.error(f"Load aibs client library error: {e}")

    # 实现callback回调函数功能，第一个参数c_int为返回值，result[0]为解引用
    @method(CFUNCTYPE(c_int, c_int, POINTER(AIBSResultST)))
    def callback_func(self, status, result):
        _data = result[0].data.decode(encoding="utf-8", errors='ignore')
        if _data is None or len(_data) == 0:
            return 0
        _type = create_string_buffer(128)
        memmove(_type, result[0].type, 128)
        real_callback_data = _type.value.decode(encoding="utf-8", errors='ignore')
        callback_data = json.loads(_data)
        callback_type = JsonUtil.parse(callback_data, 'type')
        self.notify_result(status, callback_data)

        if status == AIBSStatusCode.STATUS_AIBS_VR_CLOSED and callback_type == "VRClosed":
            with self.m_lock:
                self.m_start_cond.notify()

        if status in [AIBSStatusCode.STATUS_AIBS_INIT_SUCCESS, AIBSStatusCode.STATUS_AIBS_INIT_FAILED]:
            with self.m_lock:
                self.m_status = status
                self.m_create_cond.notify()

        if status in [AIBSStatusCode.STATUS_AIBS_SESSION_START_SUCCESS, AIBSStatusCode.STATUS_AIBS_SESSION_START_FAILED]:
            with self.m_lock:
                self.m_status = status
                self.output_start_message(status=status)

        if status == AIBSStatusCode.STATUS_AIBS_EVENT and callback_type in ["FreetalkStart", "FreetalkStop"]:
            with self.m_lock:
                self.m_freeWakeup_cond.notify()

        if status in [AIBSStatusCode.STATUS_AIBS_SESSION_FINISH] and callback_type == "LCSFinish":
            with self.m_lock:
                self.output_stop_message(status=status)

        return 0

    def output_start_message(self, status):
        self.m_start_flag = status
        async_color_print((f"--------------------- Session Start [{self.m_index}] --------------------", "yellow"))
        self.m_recorder.process_start_time = time.time()
        self.m_start_cond.notify()

    def output_stop_message(self, status):
        self.m_status = status
        self.m_finish_cond.notify()
        self.m_recorder.process_end_time = time.time()
        self.m_recorder.cpuTime = (self.m_recorder.process_end_time - self.m_recorder.process_start_time) * 1000
        if self.m_recorder.voiceTime:
            self.m_recorder.rt = self.m_recorder.cpuTime / self.m_recorder.voiceTime
        else:
            self.m_recorder.rt = 0.0
        voice_length = self.m_recorder.voiceTime / 1000.0
        cpu_time = self.m_recorder.cpuTime / 1000.0
        rt = self.m_recorder.rt
        async_color_print(("[voice length:{:.3f}(s)][cpu time:{:.3f}(s)][rt:{:.3f}]".format(voice_length, cpu_time, rt), None))
        async_color_print((f"-------------------- Session Finish [{self.m_index}] --------------------", "yellow"))
        stop_tag = 'inf'
        if self.m_region_list:
            stop_tag = pop(self.m_region_list,0)
        _data = {"source": "Mongo", "type": "ASSERTED", "voiceRegionEnd": float(stop_tag)}
        self.notify_result(MongoCode.STATUS_MONGO_FINISH, _data)
        self.m_stop_flag = status

    def set_delay(self, delay: float):
        self.m_delay = delay

    def set_listener(self, callback):
        self.m_callback = callback

    def set_timescale_callback(self, timescale_callback):
        self.m_process_timescale_callback = timescale_callback

    def set_dialog(self, status: int):
        self.m_dialog = status

    def set_finish_event(self, assert_event: Event):
        self.m_assert_finish_cond = assert_event

    def set_recorder(self, recoder: RuntimeRecorder):
        self.m_recorder = recoder

    def notify_result(self, status, callback_data):
        if self.m_callback:
            self.m_callback(status, callback_data)

    def set_voice_region_end_list(self, region_list: list):
        self.m_region_list = region_list

    def set_car_type(self, brand, device_name=None):
        if device_name:
            carType = '"{\\"brand\\":\\"%s\\",\\"device_name\\":\\"%s\\"}"' % brand, device_name
        else:
            carType = '"{\\"brand\\":\\"%s\\"}"' % brand
        return self.m_library.aibs_set_car_type(carType.encode('utf-8'))

    def create(self, config: str, lang: str, appid: str) -> int:
        """
        Create接口
        :param config: 配置文件路径
        :param lang: 语种信息
        :param appid: 语种信息
        :return: 返回状态码: 0表示成功，其他表示异常
        """
        self.m_recorder.create_start_time = datetime.now()
        self.m_status = 0
        if (not lang) or (not config) or (not appid):
            logging.error(f"Create aibs client error, lang:{lang}, config:{config}, appid:{appid}")
            return -1

        logging.info(f'Create aibs client with conf: {config}')
        self.m_engine = self.m_library.aibs_create_engine(self.callback_func, None, config.encode('utf-8'),
                                                          lang.encode('utf-8'), appid.encode('utf-8'))

        if self.m_engine is None:
            logging.error(f'Create aibs client engine failed, the engine is:{self.m_engine}')
            return -2

        with self.m_lock:
            self.m_create_cond.wait(timeout=60)
            if self.m_status == AIBSStatusCode.STATUS_AIBS_INIT_SUCCESS:
                logging.info('Create aibs engine success!')
                return 0
            else:
                logging.error(f'Create aibs engine failed, status:{self.m_status}, engine:{self.m_engine}')
                return -1

    def set_digit_status(self, digit_flag: bool):
        """
        设置引擎数字转换开关
        :param digit_flag: 数字转换开关
        :return: 返回状态码: 0表示成功，其他表示异常
        """
        self.m_digit_flag = digit_flag
        if self.m_digit_flag != -2:
            return self.set_engine_param(AIBSParam.AIBS_PARAM_DIGIT_CONVERT_RESULT, self.m_digit_flag)

    def set_engine_param(self, param: c_int, value) -> int:
        """
        设置引擎参数
        :param param: 参数类型，参考:AIBSParam
        :param value: 参数具体值
        :return: 引擎接口返回状态码
        """
        if type(value) == int:
            ret = self.m_library.set_aibs_param(self.m_engine, param, byref(c_uint(value)))
        elif type(value) == float:
            ret = self.m_library.set_aibs_param(self.m_engine, param, byref(c_float(value)))
        elif type(value) == str:
            ret = self.m_library.set_aibs_param(self.m_engine, param, c_char_p(value.encode('utf-8')))
        elif type(value) == bool:
            ret = self.m_library.set_aibs_param(self.m_engine, param, c_bool(value))
        else:
            logging.error(f'Set aibs engine parm error, unknown type of value: {type(value)}')
            return -1
        if ret != 0:
            logging.error(f'Set aibs engine parm error, ret: {ret}')
            return ret
        return 0

    def start(self, index, channel):
        """
        引擎Start接口
        :param index: 测试用例行号
        :param channel: 音频channel数
        :return: code状态码
        """
        # 判断是否在流程中，假如在流程中，则不直接调用Start接口，callback一个模拟的LCSStart信号
        if self.m_dialog in [2, 3]:
            with self.m_lock:
                self.output_start_message(status=3)
            return 0

        with self.m_lock:
            self.__reset()
            self.m_index = index
            logging.info("调用start接口...")
            ret = self.m_library.aibs_start_engine(self.m_engine, channel, None)
            self.m_recorder.startCode = ret
            self.m_start_cond.wait(timeout=5)
            clear_context = {"source":"TSA","type":"ClearContext","data":{}}
            print("ClearContext:", clear_context)
            self.set_event(clear_context)
            if self.m_status == AIBSStatusCode.STATUS_AIBS_SESSION_START_SUCCESS:
                logging.info(f'Start aibs engine start success!')
                # 设置车辆信息,必设置
                deviceID = {
                    "source": "TSA",
                    "type": "VehicleInfo",
                    "et_id": uuid(),
                    "mts": get_timestamp(),
                    "data": {
                        "vin": uuid("Mongo"),
                        "brand": "Lexus-2S",
                        "model": "T0001",
                        "originImei": "",
                        "imei": "",
                        "mmVersion": "",
                        "tsaVersion": "Mongo2.0.0"
                    }
                }
                self.set_event(deviceID)
                return 0
            elif self.m_status == AIBSStatusCode.STATUS_AIBS_SESSION_START_FAILED:
                logging.error(f'Start aibs engine start failed. ret:{ret}, pls checkout the conf.')
                return -1
            elif self.m_status == AIBSStatusCode.STATUS_AIBS_VR_CLOSED:
                return self.m_status
            else:
                logging.error(f'Start aibs engine start failed. ret:{ret}, pls checkout the conf.')
                return -1

    def process_data(self, audioPath: str, frameSize: int, timeMode) -> int:
        """
        送单声道音频
        :param audioPath: 音频路径
        :param frameSize: 每一步的读取size
        :param timeMode: 送音频方式
        :return:
        """
        if (not audioPath) or frameSize == 0:
            logging.error(f'Process data error, audio_path:{audioPath}, step_size:{frameSize}')
            return -2

        channelNum = int(frameSize / 320)
        audio_length = os.path.getsize(audioPath)
        if timeMode is not None and int(timeMode) == 1:
            self.m_timestamp = get_timestamp()

        stop_tag = None
        if self.m_region_list:
            # stop_tag = pop(self.m_region_list, 0)
            stop_tag = self.m_region_list[0]

        self.start_record()
        # 循环读取，并送音频
        with open(audioPath, 'rb') as fp:
            # 获取文件头信息
            header = fp.read(100)
            if header[:4] == b"RIFF":
                # 如果是wav文件，则获取头信息中的音频长度
                pos = re.search(b'data', header).end()
                audio_length = int.from_bytes(header[pos:pos + 4], byteorder='little')
                # 将文件指针移动到音频数据的起始位置
                fp.seek(pos + 4, 0)
            else:
                fp.seek(0, 0)
            # 实际pcm音频流数据
            remaining_bytes = audio_length
            self.m_recorder.fileSize = remaining_bytes
            self.m_recorder.voiceTime = 1000 * self.m_recorder.fileSize / (2.0 * self.m_sampleRate * channelNum)

            while remaining_bytes > 0:
                if remaining_bytes >= frameSize:
                    # 读取完整一帧数据
                    data = fp.read(frameSize)
                else:
                    # 读取剩余的不足一帧的数据
                    data = fp.read(remaining_bytes)
                
                if len(data) == 0:
                    break

                self.m_timestamp += 10
                ret = self.m_library.aibs_process_data(self.m_engine, data, len(data), c_ulong(self.m_timestamp))
                if ret != 0:
                    if self.m_status == AIBSStatusCode.STATUS_AIBS_SESSION_FINISH:
                        fp.close()
                        return 0
                    logging.error("Process data failed or the current task don't need the rest of data.")
                    print("Process data failed or the current task don't need the rest of data.")
                    return -1
                if stop_tag and stop_tag != float('inf') and self.m_timestamp == int(round(stop_tag * 1000, 0) + 3000):
                    _data = {"source": "Mongo", "type": "ASSERTED", "voiceRegionEnd": stop_tag}
                    stop_tag = pop(self.m_region_list, 0)
                    self.notify_result(MongoCode.STATUS_MONGO_ASSERT, _data)

                if stop_tag and stop_tag != float('inf') and self.m_timestamp == int(round(stop_tag * 1000, 0)):
                    self.speech_end_time = get_timestamp()

                # 相对音频时间戳触发Cancel信号
                if self.m_cancel_time == self.m_timestamp:
                    self.cancel()

                # 每送一包，把时间戳callback出去
                if self.m_process_timescale_callback:
                    self.m_process_timescale_callback(self.m_timestamp)

                # 如果存在Delay，speed=1时，每帧delay10ms（0.01s）
                delay_time = 0.01 * self.m_delay
                time.sleep(delay_time)

                remaining_bytes -= len(data)
                # 如果已经读取了指定长度，则退出循环
                if remaining_bytes <= 0:
                    break
        self.stop_record()
        return 0

    def set_tts_status(self, level: int):
        if level > 0:
            tts_status = {"source": "TSA", "type": "TTSStatus", "data": {"status": "ON_STOP", "level": int(level)}}
        else:
            tts_status = {"source": "TSA", "type": "TTSStatus", "data": {"status": "ON_STOP", "level": level}}
        self.set_event(tts_status)

    def stop(self) -> int:
        """
        停止引擎
        :return: stop状态码
        """
        # 判断是否在流程中，假如在流程中，则不调用Stop接口，callback一个模拟的LCSFinish信号
        if self.m_dialog in [1, 2]:
            with self.m_lock:
                self.set_tts_status(level=0)
                self.output_stop_message(status=240)
                self.m_dialog = 2
            return 0

        with self.m_lock:
            if self.m_dialog in [0, 3]:
                self.m_dialog = 0

            ret = self.m_library.aibs_stop_engine(self.m_engine)
            self.m_recorder.stopCode = ret
            if self.m_status == AIBSStatusCode.STATUS_AIBS_VR_CLOSED:
                return self.m_status
            if ret != 0:
                return -1
            self.m_finish_cond.wait(timeout=20)
            logging.info('Stop aibs engine stopped successfully!')
        self.m_timestamp = 0
        return 0

    def cancel(self) -> int:
        """
        引擎cancel接口封装
        :return: cancel返回状态码
        """
        self.m_recorder.cancel_start_time = time.time()
        ret = self.m_library.aibs_cancel_engine(self.m_engine, c_int(0))
        self.m_recorder.cancel_end_time = time.time()
        return ret

    def free(self) -> int:
        """
        引擎free接口封装
        :return: free接口返回状态码
        """
        return self.m_library.aibs_free_engine(self.m_engine)

    def set_personalized_info(self, file: str, option: bool) -> int:
        """
        设置个性化接口
        :param file: 个性化文件路径
        :param option: 设置开关
        :return: 设置个性化状态码
        """
        self.m_library.aibs_update_personalized_info.argtypes = [c_void_p, c_char_p, c_char_p, c_float]
        self.m_library.aibs_update_personalized_info.restype = c_int

        self.m_personal_tag = option
        if not self.m_personal_tag:
            return 0
        if self.m_status == AIBSStatusCode.STATUS_AIBS_INIT_FAILED:
            logging.error('Aibs engine created failed, please checkout before set personalized info!')
            return -1
        if not file:
            logging.error(f'Aibs engine personal tag list is none: {file}')
            return -1

        with open(file, "r", encoding="utf-8") as fp:
            if fp is None:
                logging.error(f"Set personalized info error, open tag list failed:{file}")
                return -2
            while True:
                line = fp.readline().strip()
                if not line:
                    break
                self.m_personal_tag = 0
                self.m_lock.acquire()
                try:
                    jsons = json.dumps(json.loads(line), ensure_ascii=False)
                except Exception as e:
                    print(f"parse json file [{file}] error: {e}.")
                    self.m_lock.release()
                    fp.close()
                    return -1

                self.m_recorder.personalized_start_time = time.time()
                ret = self.m_library.aibs_update_personalized_info(self.m_engine, jsons.encode('utf-8'), "utf-8".encode(), c_float(0.5))
                if ret != 0:
                    fp.close()
                    self.m_lock.release()
                    return -1

                self.m_recorder.personalized_end_time = time.time()

                # TODO 需要在回调json中解析消息并赋值self.m_personal_tag = 48
                if self.m_personal_tag == 48:
                    self.m_lock.release()
                    self.m_recorder.personalized_load_time = self.m_recorder.personalized_end_time - self.m_recorder.personalized_start_time
                    print("m_recorder->tagLoadTime:%d\n", self.m_recorder.personalized_load_time)
                    fp.close()
                    return 0

                # TODO 需要在回调json中解析消息并赋值self.m_personal_tag = 49
                elif self.m_personal_tag == 49:
                    self.m_lock.release()
                    logging.error(f"Set personal tag list failed:{file}")
                    fp.close()
                    return -1
                else:
                    self.m_lock.release()
                    fp.close()
                    return 0
        fp.close()
        return 0

    def set_freetalk_status(self, status):
        if status not in [0, 1]:
            logging.error("Set free talk error, status:", status)
            return -1
        with self.m_lock:
            free_wakeup_data = {"source": "TSA", "type": "SetFreeWakeup", "data": {"value": 63, "status": status}}
            self.set_event(free_wakeup_data)
            self.m_freeWakeup_cond.wait(timeout=20)
            return 0

    def set_hmi(self, file: str, app_name: str) -> int:
        """
        设置可见即可说页面数据
        :param file: hmi json文本
        :param app_name: app_name
        :return: 返回状态码
        """
        if not file:
            logging.error(f'Set hmi file is empty! check the path:[{file}]')
            return -1
        if not app_name:
            logging.error(f'Set hmi app_name is empty! pls check:[{app_name}]')
            return -1
        data = json.loads(file)
        tag_json = json.dumps(data)
        if len(tag_json) == 0:
            logging.error(f'The hmi data in the file is empty! check the file:[{file}]')
            return -1

        self.m_lock.acquire()
        ret = self.m_library.aibs_set_page_intent(self.m_engine, tag_json.encode(), app_name.encode())
        if ret != 0:
            self.m_lock.release()
            logging.error(f'Set hmi json data[{file}] failed! code:[{ret}]')
            return -1
        self.m_lock.release()
        return 0

    def set_language(self, mode: str):
        """
        动态设置语种信息
        :param mode: 语种，可选值：eng, cmn, yue
        :return: 返回状态码，0表示成功，其他表示失败
        """
        # 如果之前的Case已经设置过语种了，并且和当前的语种相等，则跳过设置语种的逻辑
        if self.default_language is not None and self.default_language == mode:
            return 0
        
        ret = self.m_library.aibs_set_language_mode(self.m_engine, mode.encode())
        if ret != 0:
            logging.error(f'set language error ret: {ret}')
            return ret
        
        self.default_language = mode
        return 0

    def set_vr_reset(self):
        """
        恢复出厂设置
        :return: 返回状态码，0表示成功，其他表示失败
        """
        return self.m_library.aibs_clear_vr_config(self.m_engine)

    def set_vr_config(self, param: c_int, value):
        """
        设置VR配置项
        :param param: 参数类型，参考:AIBSVRConfigCode
        :param value: 参数具体值
        :return: 设置VR配置项接口返回状态码
        """
        if type(value) == int:
            ret = self.m_library.aibs_set_vr_config(self.m_engine, param, byref(c_uint(value)))
        elif type(value) == str:
            ret = self.m_library.aibs_set_vr_config(self.m_engine, param, c_char_p(value.encode('utf-8')))
        else:
            logging.error(f'Set vr config parm error, unknown type of value: {type(value)}')
            return -1
        if ret != 0:
            logging.error(f'Set vr config parm error, ret: {ret}')
            return ret
        else:
            for name in dir(AIBSVRConfigCode):
                val = getattr(AIBSVRConfigCode, name)
                if isinstance(val, c_int) and val.value == param.value:
                    with allure.step(f"VR配置项【{name}】设置结果：{self.get_vr_config(param)}"): pass
                    break
            return 0

    def get_vr_config(self, key):
        """
        根据传入的配置项获取对应的值
        :return: 返回状态码，0表示成功，其他表示失败
        """
        self.m_library.aibs_get_vr_config.restype = c_char_p
        address = self.m_library.aibs_get_vr_config(self.m_engine, key)
        if address:
            if key == AIBSVRConfigCode.AIBS_SETTING_GET_WAKEUP_WORD:
                return address.decode(encoding='utf-8')
            return address.decode(encoding='unicode_escape')
        else:
            return None

    def set_wakeup_word(self, word: str, threshold: float) -> int:
        """
        设置自定义唤醒词接口
        :param word: 自定义唤醒词
        :param threshold: 阈值
        :return: 状态码，0表示成功，其他表示失败
        """
        return self.m_library.aibs_set_wakeup_word(self.m_engine, word.encode(), c_float(threshold))

    def set_wakeup_word_enable(self, word: str, option) -> int:
        return self.m_library.aibs_set_wakeup_word_enable(self.m_engine, word.encode(), c_bool(option))

    def get_version(self):
        self.m_library.aibs_get_engine_version.restype = c_char_p
        data = self.m_library.aibs_get_engine_version(self.m_engine)
        return data.decode(encoding="utf-8")

    def get_wakeup_word_list(self):
        """
        获取唤醒词列表
        :return: 成功则返回wakeup word，否则返回NULL
        """
        result = []
        self.m_library.aibs_get_wakeup_word.restype = c_char_p
        ref = self.m_library.aibs_get_wakeup_word(self.m_engine)
        try:
            data = ref.decode(encoding="utf-8")
            wakeup_list = json.loads(data).get("wakeup").get("data").get("fix_data")
            for i in wakeup_list:
                result.append(i.get("word"))
            return result
        except Exception as e:
            logging.error(f"Get wakeup list error:{e}")
            return []

    def send_text(self, text) -> int:
        if not text:
            return -1
        text_request = {"source": "TSA", "type": "TextRequest", "data": {"text": text}}
        return self.set_event(text_request)

    def set_event_list(self, events, is_file: bool, et_id=None) -> int:
        try:
            if is_file:
                with open(events, 'r', encoding='utf-8') as fp:
                    for line in fp:
                        event_list_data = json.loads(line)
                        ret = self.set_event(event_list_data, et_id=et_id)
                        if ret != 0:
                            raise Exception(f"ret: {ret}")
            else:
                for data in events:
                    ret = self.set_event(data, et_id=et_id)
                    if ret != 0:
                        raise Exception(f"ret: {ret}")
        except Exception as e:
            logging.error(f"Set event error, parse json data error:{e}")
            return -1
        return 0

    def set_event(self, event, is_file: bool = False, et_id=None) -> int:
        try:
            if is_file:
                with open(event, 'r', encoding='utf-8') as fp:
                    dict_data = json.load(fp)
            else:
                dict_data = event

            et_json = {"et_id": et_id if et_id else uuid(), "mts": get_timestamp()}
            dict_data.update(et_json)
            json_data = json.dumps(dict_data, ensure_ascii=False, separators=(',', ':'))
            with allure.step(f"发送事件：{dict_data.get('type')}"):
                with allure.step(f"{json_data}"):
                    pass
            return self.m_library.aibs_set_event(self.m_engine, json_data.encode('utf-8'))
        except Exception as e:
            logging.error(f"Set event error, parse json data error:{e}")

    def set_cancel_tag(self, time_tag: int):
        self.m_cancel_time = time_tag

    def set_work_mode(self, param: str) -> int:
        """
        字符串设置，设置引擎工作模式
        :param: 引擎工作模式，字符串形式
        :return: speechEngine返回状态码
        """
        try:
            work_mode = int(param)
            if work_mode not in [0, 1, 2, 3, 4]:
                raise ValueError
        except ValueError:
            logging.error(f"Unknown work mode：{param}.")
            return -1
        return self.m_library.aibs_set_workmode(self.m_engine, c_int(work_mode))

    def __reset(self):
        self.m_status = 0
        self.m_start_flag = 0
        self.m_stop_flag = 0

    def start_speaker_enroll(self, user_id, text, id, channel_id):
        self.m_library.aibs_start_speaker_enroll.argtypes = [c_void_p, c_char_p, c_char_p, c_int, c_int]
        self.m_library.aibs_start_speaker_enroll.restype = c_int
        return self.m_library.aibs_start_speaker_enroll(self.m_engine, user_id.encode(), text.encode(), id, channel_id)

    def end_speaker_enroll(self):
        self.m_library.aibs_end_speaker_enroll.argtypes = [c_void_p]
        self.m_library.aibs_end_speaker_enroll.restype = c_int
        return self.m_library.aibs_end_speaker_enroll(self.m_engine)

    def recognize_speaker(self, channel_id, start_time, end_time):
        self.m_library.aibs_recognize_speaker.argtypes = [c_void_p, c_int, c_long, c_long]
        self.m_library.aibs_recognize_speaker.restype = c_char_p
        result = self.m_library.aibs_recognize_speaker(self.m_engine, channel_id, start_time, end_time)
        if result:
            json_data = result.decode(encoding="utf-8")
            user_id = json.loads(json_data).get("data").get("user_id")
            return user_id
        else:
            return None

    def delete_speaker_info(self, user_id):
        self.m_library.aibs_delete_speaker_info.argtypes = [c_void_p, c_char_p]
        self.m_library.aibs_delete_speaker_info.restype = c_int
        return self.m_library.aibs_delete_speaker_info(self.m_engine, user_id.encode())

    def get_speaker_info(self, user_id):
        self.m_library.aibs_get_speaker_info.argtypes = [c_void_p, c_char_p]
        self.m_library.aibs_get_speaker_info.restype = c_char_p
        result = self.m_library.aibs_get_speaker_info(self.m_engine, user_id.encode())
        if result:
            json_data = result.decode(encoding="utf-8")
            user_id = json.loads(json_data).get("data").get("user_id")
            return user_id
        else:
            return None

    def get_enroll_text(self):
        self.m_library.aibs_get_enroll_text.argtypes = [c_void_p]
        self.m_library.aibs_get_enroll_text.restype = c_char_p
        result =  self.m_library.aibs_get_enroll_text(self.m_engine)
        return result.decode(encoding="utf-8")

    def aibs_sensitive_word_judgment(self, word):
        self.m_library.aibs_sensitive_word_judgment.argtypes = [c_void_p, c_char_p]
        self.m_library.aibs_sensitive_word_judgment.restype = c_int
        result =  self.m_library.aibs_sensitive_word_judgment(self.m_engine, word.encode())
        return result

    def aibs_get_registered_speaker(self):
        self.m_library.aibs_get_registered_speaker.argtypes = [c_void_p]
        self.m_library.aibs_get_registered_speaker.restype = c_char_p
        result =  self.m_library.aibs_get_registered_speaker(self.m_engine)
        return result.decode(encoding="utf-8")

    def open_voice_input(self, param):
        json_param = None
        try:
            json_param = json.dumps(param, ensure_ascii=False)
        except Exception as e:
            logging.error(f"Set event error, parse json data error:{e}")

        self.m_library.aibs_open_voice_input.argtypes = [c_void_p, c_char_p]
        self.m_library.aibs_open_voice_input.restype = c_int
        return self.m_library.aibs_open_voice_input(self.m_engine, json_param.encode())

    def close_voice_input(self):
        self.m_library.aibs_close_voice_input.argtypes = [c_void_p]
        self.m_library.aibs_close_voice_input.restype = c_int
        return self.m_library.aibs_close_voice_input(self.m_engine)

    def start_record(self):
        self.m_library.aibs_start_record.argtypes = [c_void_p]
        self.m_library.aibs_start_record.restype = c_int
        return self.m_library.aibs_start_record(self.m_engine)

    def stop_record(self):
        self.m_library.aibs_stop_record.argtypes = [c_void_p]
        self.m_library.aibs_stop_record.restype = c_int
        return self.m_library.aibs_stop_record(self.m_engine)
